import time, sys, os, struct
from threading import Thread, Lock
from pickle import dumps, loads

sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
#from bitbrook.utilities.limited_socket import LSocket
import socket

COMMAND_FORMAT = "!16sI"

class NetworkBuilder(Thread):
    def __init__(self, network, brook):
        super(NetworkBuilder, self).__init__()
        #needed for thread stopping
        self._shutdown = False
        self._shutdown_signal = Lock()
        self._get_info_lock = Lock()
        #self._send_info_lock = Lock()
        
        self._network = network
        self._brook = brook
    
    def run(self):
        with self._shutdown_signal:
            while not self._shutdown:
                random_peer = self._network.get_random_peer()
                if not random_peer == None and not random_peer == self._network.my_addr:
                    self.get_info(random_peer)
                time.sleep(5)
    '''
    it's assumed that peer is not your own address or None
    '''
    def get_info(self, peer):
        print "getting info from: ", peer
        with self._get_info_lock:
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
            #sock = LSocket()
            sock.connect(peer)
            my_addr = dumps(self._network.my_addr)
            request = struct.pack(COMMAND_FORMAT, "GOSSIP", len(my_addr))
            self._send(sock, request)
            answer = self._recieve(sock, 0)
            if not answer == "ACK":
                print "Error 1"
                return None
            self._send(sock, my_addr)
            answer = self._recieve(sock, 0)
            if not answer == "ACK":
                print "Error 2"
                return None
            self._send(sock, "ACK")
            answer = self._recieve(sock, 0)
            _, leng = struct.unpack(COMMAND_FORMAT, answer)
            self._send(sock, "ACK")
            data = self._recieve(sock, leng)
            (chunks, rand_peer) = loads(data)
            if not self._network.know_peer(rand_peer):
                print "Random Peer not known, adding"
                self._network.add_peer(rand_peer)
            self._network.set_peer_data(peer, chunks)

    def shutdown(self, block = True):
        print "Shutting down Network Builder"
        self._shutdown = True
        if block:
            self._shutdown_signal.acquire()
            self._shutdown_signal.release()
    
    def _send(self, sock, data):
        sent = 0
        while sent < len(data):
            sent = sent + sock.send(data[sent:])
    
    def _recieve(self, sock, leng):
        if leng == 0:
            tries = 0
            while tries < 4:
                try:
                    return sock.recv(1024)
                except BaseException as e:
                    tries += 1
                    if tries == 4:
                        raise e
        else:
            data = ""
            while len(data) < leng:
                data = data + sock.recv(1024)
            return data

class InfoSender(Thread):
    def __init__(self, network, brook, sock, leng):
        super(InfoSender, self).__init__()
        self._send_info_lock = Lock()
        self._network = network
        self._brook = brook
        self._sock = sock
        self._first_payload = leng
    
    def run(self):
        with self._send_info_lock:
            self._send("ACK")
            answer = self._recieve(self._first_payload)
            remote_peer = loads(answer)
            if not self._network.know_peer(remote_peer):
                print "Remote peer not known, adding"
                self._network.add_peer(remote_peer)
            self._send("ACK")
            answer = self._recieve(0)
            if not answer == "ACK":
                print "Error 3"
                return None
            my_chunks = self._brook.chunk_completed
            rand_peer = self._network.get_random_peer()
            data = dumps((my_chunks, rand_peer))
            response = struct.pack(COMMAND_FORMAT, "GOSSIP", len(data))
            self._send(response)
            answer = self._recieve(0)
            if not answer == "ACK":
                print "Error 4"
                return None
            self._send(data)
    
    def _send(self, data):
        sent = 0
        while sent < len(data):
            sent = sent + self._sock.send(data[sent:])
    
    def _recieve(self, leng):
        if leng == 0:
            tries = 0
            while tries < 4:
                try:
                    return self._sock.recv(1024)
                except BaseException as e:
                    tries += 1
                    if tries == 4:
                        raise e
        else:
            data = ""
            while len(data) < leng:
                data = data + self._sock.recv(1024)
            return data